// import fs from 'fs'
const fs = {}
import utils from '../utils'
import { BinArray } from '../utils'
import ShpConverterText from '../shpconverter-text'

function readFirstChars(reader, n) {
  return ShpConverterText.bufferToString(reader.readSync(0, Math.min(n || 1000, reader.size())))
}
// Same interface as FileReader, for reading from a Buffer or ArrayBuffer instead of a file.
function BufferReader(src) {
  const bufSize = src.byteLength || src.length
  let binArr = null
  let buf = null
  this.readToBinArray = function(start, length) {
    if (bufSize < start + length) utils.error("Out-of-range error")
    if (!binArr) binArr = new BinArray(src)
    binArr.position(start)
    return binArr
  }
  this.toString = function(enc) {
    return ShpConverterText.bufferToString(buffer(), enc)
  }
  this.readSync = function(start, length) {
    // TODO: consider using a default length like FileReader
    return buffer().slice(start, length || bufSize)
  }
  function buffer() {
    if (!buf) {
      buf = (src instanceof ArrayBuffer) ? utils.createBuffer(src) : src
    }
    return buf
  }
  this.findString = FileReader.prototype.findString
  this.expandBuffer = function() {return this}
  this.size = function() {return bufSize}
  this.close = function() {}
}
function FileReader(path, opts) {
  const fileLen = fs.statSync(path).size
  const DEFAULT_CACHE_LEN = opts && opts.cacheSize || 0x800000
  let DEFAULT_BUFFER_LEN = opts && opts.bufferSize || 0x4000
  let fd = null
  let cacheOffs = null
  let cache = null
  let binArr = null
  this.expandBuffer = function() {
    DEFAULT_BUFFER_LEN *= 2
    return this
  }
  // Read to BinArray (for compatibility with ShpReader)
  this.readToBinArray = function(start, length) {
    if (updateCache(start, length)) {
      binArr = new BinArray(cache)
    }
    binArr.position(start - cacheOffs)
    return binArr
  }
  // Read to Buffer
  this.readSync = function(start, length) {
    if (length > 0 === false) {
      // use default (but variable) size if length is not specified
      length = DEFAULT_BUFFER_LEN
      if (start + length > fileLen) {
        length = fileLen - start // truncate at eof
      }
      if (length === 0) {
        return utils.createBuffer(0) // kludge to allow reading up to eof
      }
    }
    updateCache(start, length)
    return cache.slice(start - cacheOffs, start - cacheOffs + length)
  }
  this.size = function() {
    return fileLen
  }

  this.close = function() {
    if (fd) {
      fs.closeSync(fd)
      fd = null
      cache = null
    }
  }
  // Receive offset and length of byte string that must be read
  // Return true if cache was updated, or false
  function updateCache(fileOffs, bufLen) {
    const headroom = fileLen - fileOffs
    if (headroom < bufLen || headroom < 0) {
      utils.error("Tried to read past end-of-file")
    }
    if (cache && fileOffs >= cacheOffs && cacheOffs + cache.length >= fileOffs + bufLen) {
      return false
    }
    let bytesToRead = Math.max(DEFAULT_CACHE_LEN, bufLen)
    if (headroom < bytesToRead) {
      bytesToRead = headroom
    }
    if (!cache || bytesToRead != cache.length) {
      cache = utils.createBuffer(bytesToRead)
    }
    if (!fd) {
      fd = fs.openSync(path, 'r')
    }
    const bytesRead = fs.readSync(fd, cache, 0, bytesToRead, fileOffs)
    cacheOffs = fileOffs
    if (bytesRead != bytesToRead) utils.error("Error reading file")
    return true
  }
}
FileReader.prototype.findString = function (str, maxLen) {
  const len = Math.min(this.size(), maxLen || this.size())
  const buf = this.readSync(0, len)
  const strLen = str.length
  const n = buf.length - strLen
  const firstByte = str.charCodeAt(0)
  for (let i=0; i < n; i++) {
    if (buf[i] == firstByte && buf.toString('utf8', i, i + strLen) == str) {
      return {
        offset: i + strLen,
        text: buf.toString('utf8', 0, i)
      }
    }
  }
  return null
}
export {
  BufferReader,
}
export default {
  readFirstChars,
}